import * as puppeteer from 'puppeteer';

/**
 * 页面操作工具类
 */
export class PageUtils {
  /**
   * 安全地等待页面元素出现
   */
  static async waitForSelector(
    page: puppeteer.Page,
    selector: string,
    timeout: number = 10000,
  ): Promise<puppeteer.ElementHandle<Element> | null> {
    try {
      return await page.waitForSelector(selector, { timeout });
    } catch (error) {
      console.warn(`等待元素失败: ${selector}`, error);
      return null;
    }
  }

  /**
   * 安全地评估页面脚本
   */
  static async safeEvaluate<T>(
    page: puppeteer.Page,
    evaluateFn: () => T,
    errorMessage: string = '页面脚本执行失败',
  ): Promise<T | null> {
    try {
      return await page.evaluate(evaluateFn);
    } catch (error) {
      console.warn(errorMessage, error);
      return null;
    }
  }

  static async safeEvaluateWithArgs<T, A extends any[]>(
    page: puppeteer.Page,
    evaluateFn: (...args: A) => T,
    args: A,
    errorMessage: string = '页面脚本执行失败',
  ): Promise<T | null> {
    try {
      return await page.evaluate(evaluateFn, ...args);
    } catch (error) {
      console.warn(errorMessage, error);
      return null;
    }
  }

  /**
   * 延迟函数
   */
  static async delay(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  /**
   * 设置页面用户代理和视口
   */
  static async setupPage(page: puppeteer.Page): Promise<void> {
    await page.setUserAgent(
      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    );
    await page.setViewport({ width: 1920, height: 1080 });
  }

  /**
   * 智能等待页面加载完成
   */
  static async waitForPageLoad(
    page: puppeteer.Page,
    options: {
      waitTime?: number;
      waitCondition?: () => Promise<boolean>;
      maxWaitTime?: number;
    } = {},
  ): Promise<void> {
    const { waitTime = 3000, maxWaitTime = 30000 } = options;

    try {
      // 首先等待网络空闲
      await page.waitForNavigation({
        waitUntil: 'networkidle0',
        timeout: 5000,
      });
    } catch (error) {
      // 如果网络空闲等待失败，继续执行
    }

    // 等待指定时间
    await new Promise((resolve) => setTimeout(resolve, waitTime));

    // 如果提供了自定义等待条件，使用自定义条件
    if (options.waitCondition) {
      const startTime = Date.now();
      while (Date.now() - startTime < maxWaitTime) {
        try {
          const conditionMet = await options.waitCondition();
          if (conditionMet) break;
        } catch (error) {
          // 忽略条件检查错误
        }
        await new Promise((resolve) => setTimeout(resolve, 500));
      }
    }
  }

  /**
   * 批量提取元素文本内容
   */
  static async extractTexts(
    page: puppeteer.Page,
    selector: string,
    options: {
      filter?: (text: string) => boolean;
      transform?: (text: string) => string;
    } = {},
  ): Promise<string[]> {
    return await page.evaluate(
      (sel, opts) => {
        const elements = document.querySelectorAll(sel);
        return Array.from(elements)
          .map((el) => el.textContent?.trim() || '')
          .filter((text) => text.length > 0)
          .filter(opts.filter || (() => true))
          .map(opts.transform || ((text) => text));
      },
      selector,
      options,
    );
  }

  /**
   * 检查页面是否包含反爬虫措施
   */
  static async detectAntiCrawler(page: puppeteer.Page): Promise<boolean> {
    const indicators = await page.evaluate(() => {
      const checks = [
        // 检查是否有验证码相关元素
        !!document.querySelector('[class*="captcha"], [id*="captcha"]'),
        // 检查是否有404或错误页面标识
        document.title.includes('404') || document.title.includes('错误'),
        // 检查页面内容是否过少
        document.body.innerText.length < 100,
        // 检查是否有重定向脚本
        !!document.querySelector('script[src*="redirect"]'),
        // 检查是否有访问限制提示
        document.body.innerText.includes('访问频率') ||
          document.body.innerText.includes('请稍后') ||
          document.body.innerText.includes('验证'),
      ];

      return checks.some((check) => check);
    });

    return indicators;
  }

  /**
   * 处理反爬虫措施
   */
  static async handleAntiCrawler(page: puppeteer.Page): Promise<void> {
    // 增加随机延迟
    await new Promise((resolve) =>
      setTimeout(resolve, Math.random() * 2000 + 1000),
    );

    // 尝试刷新页面
    await page.reload({ waitUntil: 'networkidle0' });

    // 再次等待
    await new Promise((resolve) => setTimeout(resolve, 3000));
  }

  /**
   * 批量操作页面，自动处理重试和错误
   */
  static async batchOperation<T, R>(
    items: T[],
    operation: (item: T) => Promise<R>,
    options: {
      batchSize?: number;
      delayBetweenBatches?: number;
      maxRetries?: number;
      retryDelay?: number;
    } = {},
  ): Promise<R[]> {
    const {
      batchSize = 5,
      delayBetweenBatches = 1000,
      maxRetries = 3,
      retryDelay = 2000,
    } = options;

    const results: R[] = [];

    for (let i = 0; i < items.length; i += batchSize) {
      const batch = items.slice(i, i + batchSize);

      for (const item of batch) {
        let retries = 0;
        let lastError: Error | null = null;

        while (retries < maxRetries) {
          try {
            const result = await operation(item);
            results.push(result);
            break;
          } catch (error) {
            lastError = error as Error;
            retries++;
            if (retries < maxRetries) {
              await new Promise((resolve) => setTimeout(resolve, retryDelay));
            }
          }
        }

        if (retries >= maxRetries && lastError) {
          console.error(`批量操作失败，已重试${maxRetries}次:`, lastError);
        }
      }

      // 批次间延迟
      if (i + batchSize < items.length) {
        await new Promise((resolve) =>
          setTimeout(resolve, delayBetweenBatches),
        );
      }
    }

    return results;
  }

  /**
   * 截取页面截图用于调试
   */
  static async takeScreenshot(
    page: puppeteer.Page,
    filename: string,
    options?: puppeteer.ScreenshotOptions,
  ): Promise<void> {
    try {
      // 确保截图目录存在
      const fs = require('fs');
      const path = require('path');
      const screenshotDir = path.join(process.cwd(), 'debug-screenshots');

      if (!fs.existsSync(screenshotDir)) {
        fs.mkdirSync(screenshotDir, { recursive: true });
      }

      // 确保文件名有正确的扩展名
      const finalFilename =
        filename.endsWith('.png') ||
        filename.endsWith('.jpeg') ||
        filename.endsWith('.webp')
          ? filename
          : `${filename}.png`;

      const fullPath = path.join(screenshotDir, finalFilename);

      await page.screenshot({
        path: fullPath,
        fullPage: true,
        ...options,
      });
    } catch (error) {
      console.warn(`截图失败: ${filename}`, error);
    }
  }

  /**
   * 获取页面控制台日志
   */
  static async captureConsoleLogs(page: puppeteer.Page): Promise<string[]> {
    const logs: string[] = [];

    page.on('console', (msg) => {
      logs.push(`[${msg.type()}] ${msg.text()}`);
    });

    page.on('pageerror', (error) => {
      logs.push(`[ERROR] ${error.message}`);
    });

    return logs;
  }
}
